package com.qen.wrapper.update;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.qen.common.BeetlUtils;
import com.qen.common.LambdaUtils;
import com.qen.common.LogicInfoUtils;
import com.qen.common.TableList;
import com.qen.enums.SqlKeyword;
import com.qen.interfaces.MFunction;
import com.qen.interfaces.SFunction;
import com.qen.segments.FuncConsumer;
import com.qen.wrapper.JoinAbstractLambdaWrapper;
import com.qen.wrapper.SharedString;
import com.qen.wrapper.segments.MergeSegments;
import com.qen.wrapper.update.interfaces.Update;
import lombok.Data;
import org.beetl.sql.clazz.ClassDesc;
import org.beetl.sql.clazz.NameConversion;
import org.beetl.sql.clazz.kit.BeanKit;
import org.beetl.sql.core.SQLManager;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.qen.constant.DataBaseConstants.DASH;
import static com.qen.constant.DataBaseConstants.PLUS;

/**
 * @author licz
 * @since 1.0.0
 */
@SuppressWarnings("unused")
public class UpdateJoinWrapper<T>
        extends JoinAbstractLambdaWrapper<T, UpdateJoinWrapper<T>>
        implements Update<UpdateJoinWrapper<T>> {
    /**
     * SQL 更新字段内容，例如：name='1', age=2
     */
    private final SharedString sqlSetStr = new SharedString();
    /**
     * SQL 更新字段内容，例如：name='1', age=2
     */
    private List<String> sqlSet;
    /**
     * SQL 更新字段内容，例如：name='1', age=2
     */
    private List<UpdateSet> updateSet;
    /**
     * SQL 更新实体(更新非空字段)
     */
    private List<Object> updateEntity;
    /**
     * SQL 更新实体(空字段也会更新)
     */
    private List<Object> updateEntityNull;

    private UpdateJoinWrapper() {
        super();
    }

    /**
     * 推荐使用此构造方法
     */
    public UpdateJoinWrapper(Class<T> clazz) {
        super(clazz);
    }

    public UpdateJoinWrapper(T entity) {
        super(entity);
    }

    public UpdateJoinWrapper(Class<T> clazz, String alias) {
        super(clazz, alias);
    }

    public UpdateJoinWrapper(T entity, String alias) {
        super(entity, alias);
    }

    /**
     * 设置更新的实体set语句部分, 更新非空字段
     * <p>
     * 注意!!!
     * 这里这是的实体类是set部分, 不作为条件, where条件是wrapper.setEntity()
     */
    public UpdateJoinWrapper<T> setUpdateEntity(Object... entity) {
        if (Objects.isNull(updateEntity)) {
            updateEntity = new ArrayList<>();
        }
        for (Object obj : entity) {
            Assert.notNull(obj, "更新实体不能为空");
            updateEntity.add(obj);
        }
        return typedThis;
    }

    /**
     * 设置更新的实体set语句部分, 更新非空字段
     * <p>
     * 注意!!!
     * 这里这是的实体类是set部分, 不作为条件, where条件是wrapper.setEntity()
     */
    public UpdateJoinWrapper<T> setUpdateEntityAndNull(Object... entity) {
        if (Objects.isNull(updateEntityNull)) {
            updateEntityNull = new ArrayList<>();
        }
        for (Object obj : entity) {
            Assert.notNull(obj, "更新实体不能为空");
            updateEntityNull.add(obj);
        }
        return typedThis;
    }

    @Override
    public <R> UpdateJoinWrapper<T> set(boolean condition, SFunction<R, ?> column, Object val, String mapping) {
        return maybeDo(condition, () -> getUpdateSet().add(new UpdateSet(column, val, mapping, false, null)));
    }

    @Override
    public <R, V> UpdateJoinWrapper<T> set(boolean condition, SFunction<R, ?> column, SFunction<V, ?> val, String mapping) {
        return maybeDo(condition, () -> getUpdateSet().add(new UpdateSet(column, val, mapping, false, null)));
    }

    @Override
    public <R> UpdateJoinWrapper<T> setIncrBy(boolean condition, SFunction<R, ?> column, Number val) {
        return maybeDo(condition, () -> getUpdateSet().add(new UpdateSet(column, val, null, true, PLUS)));
    }

    @Override
    public <R> UpdateJoinWrapper<T> setDecrBy(boolean condition, SFunction<R, ?> column, Number val) {
        return maybeDo(condition, () -> getUpdateSet().add(new UpdateSet(column, val, null, true, DASH)));
    }

    @Override
    public UpdateJoinWrapper<T> setApply(boolean condition, String applySql, MFunction<FuncConsumer> consumerFunction, Object... values) {
        if (condition && StrUtil.isNotBlank(applySql)) {
            FuncConsumer funcConsumer = consumerFunction.apply(new FuncConsumer());
            UpdateSet set = new UpdateSet();
            set.setApply(true);
            set.setFormat(applySql);
            set.setColumns(funcConsumer.getArgs());
            set.setArgs(ArrayUtil.isNotEmpty(values) ? values : funcConsumer.getValues());
            getUpdateSet().add(set);
        }
        return typedThis;
    }

    @Override
    public UpdateJoinWrapper<T> setSql(boolean condition, String sql) {
        if (condition && StrUtil.isNotBlank(sql)) {
            if (Objects.isNull(sqlSet)) {
                sqlSet = new ArrayList<>();
            }
            sqlSet.add(sql);
        }
        return typedThis;
    }

    @SuppressWarnings("DuplicatedCode")
    @Override
    public String getSqlSet() {
        if (StrUtil.isNotBlank(sqlSetStr.getStringValue())) {
            return sqlSetStr.getStringValue();
        }
        StringBuilder set = new StringBuilder(StrUtil.EMPTY);
        if (CollectionUtil.isNotEmpty(updateSet)) {
            String setSql = updateSet.stream().map(i -> {
                if (i.isApply) {
                    String col = String.format(i.format, Arrays.stream(i.columns).map(f ->
                            tableList.getPrefixByClass(LambdaUtils.getEntityClass(f)) +
                                    StrUtil.DOT + getCache(f).getColumn()).toArray());
                    return formatSqlMaybeWithParam(col, i.args);
                } else {
                    String col = tableList.getPrefixByClass(LambdaUtils.getEntityClass(i.getColumn())) +
                            StrUtil.DOT + getCache(i.getColumn()).getColumn();
                    if (i.incOrDnc) {
                        return col + SqlKeyword.EQ.getSqlSegment() + col + i.cal + i.value;
                    } else {
                        if (i.value instanceof Function) {
                            SFunction<?, ?> value = (SFunction<?, ?>) i.getValue();
                            return col + SqlKeyword.EQ.getSqlSegment() + tableList.getPrefixByClass(LambdaUtils.getEntityClass(value)) +
                                    StrUtil.DOT + getCache(value).getColumn();
                        } else {
                            return col + SqlKeyword.EQ.getSqlSegment() + formatParam(i.mapping, i.value);
                        }
                    }
                }
            }).collect(Collectors.joining(StrUtil.COMMA)) + StrUtil.COMMA;
            set.append(setSql);
        }
        if (CollectionUtil.isNotEmpty(sqlSet)) {
            set.append(String.join(StrUtil.COMMA, sqlSet)).append(StrUtil.COMMA);
        }
        if (CollectionUtil.isNotEmpty(updateEntity)) {
            getSqlByEntity(set, true, updateEntity);
        }
        if (CollectionUtil.isNotEmpty(updateEntityNull)) {
            getSqlByEntity(set, false, updateEntityNull);
        }
        sqlSetStr.setStringValue(set.toString());
        return set.toString();
    }

    /**
     * 用于生成嵌套 sql
     * <p>故 sqlSelect 不向下传递</p>
     */
    @Override
    protected UpdateJoinWrapper<T> instance() {
        return instance(index, null, null, null);
    }

    @Override
    protected UpdateJoinWrapper<T> instanceEmpty() {
        return new UpdateJoinWrapper<>();
    }

    @Override
    protected UpdateJoinWrapper<T> instance(Integer index, String keyWord, Class<?> joinClass, String tableName) {
        return new UpdateJoinWrapper<>(getEntity(), getEntityClass(), paramNameSeq, paramNameValuePairs,
                new MergeSegments(), SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString(),
                this.tableList, index, keyWord, joinClass, tableName);
    }

    public List<UpdateSet> getUpdateSet() {
        if (updateSet == null) {
            updateSet = new ArrayList<>();
        }
        return updateSet;
    }

    /**
     * 不建议直接 new 该实例，使用 JoinWrappers.update(User.class)
     */
    protected UpdateJoinWrapper(T entity, Class<T> entityClass, AtomicInteger paramNameSeq,
                                Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments,
                                SharedString lastSql, SharedString sqlComment, SharedString sqlFirst,
                                TableList tableList, Integer index, String keyWord, Class<?> joinClass, String tableName) {
        super.setEntity(entity);
        super.setEntityClass(entityClass);
        this.paramNameSeq = paramNameSeq;
        this.paramNameValuePairs = paramNameValuePairs;
        this.expression = mergeSegments;
        this.lastSql = lastSql;
        this.sqlComment = sqlComment;
        this.sqlFirst = sqlFirst;
        this.tableList = tableList;
        this.index = index;
        this.keyWord = keyWord;
        this.joinClass = joinClass;
        this.tableName = tableName;
    }

    @SuppressWarnings("DuplicatedCode")
    private void getSqlByEntity(StringBuilder sb, boolean filterNull, List<Object> entityList) {
        for (Object obj : entityList) {
            Assert.isTrue(tableList.contain(obj.getClass()), "更新的实体不是主表或关联表 <%s>", obj.getClass().getSimpleName());
            SQLManager sqlManager = BeetlUtils.getSQLManager();
            NameConversion nc = sqlManager.getNc();
            Class<?> clz = obj.getClass();
            ClassDesc classDesc = sqlManager.getClassDesc(clz);
            Field field = LogicInfoUtils.getFieldByPojo(clz);
            String col = null;
            if (field != null) {
                col = field.getName();
            }
            for (String attr : classDesc.getAttrs()) {
                String inCol = nc.getColName(obj.getClass(), attr);
                if (inCol.equals(col)) {
                    continue;
                }
                Object val = BeanKit.getBeanProperty(obj, attr);
                if (filterNull && Objects.isNull(val)) {
                    continue;
                }
                sb.append(tableList.getPrefixByClass(obj.getClass())).append(StrUtil.DOT)
                        .append(inCol).append(SqlKeyword.EQ.getSqlSegment()).append(val)
                        .append(StrUtil.COMMA);
            }
//            TableInfo tableInfo = TableHelper.getAssert(obj.getClass());
//            for (TableFieldInfo fieldInfo : tableInfo.getFieldList()) {
//                if (AdapterHelper.getAdapter().mpjHasLogic(tableInfo) && fieldInfo.isLogicDelete()) {
//                    continue;
//                }
//                Object val;
//                try {
//                    Field field = AdapterHelper.getAdapter().mpjGetField(fieldInfo, () -> {
//                        Field field1 = ReflectionKit.getFieldMap(obj.getClass()).get(fieldInfo.getProperty());
//                        field1.setAccessible(true);
//                        return field1;
//                    });
//                    val = field.get(obj);
//                } catch (IllegalAccessException e) {
//                    throw new RuntimeException(e);
//                }
//                if (filterNull && Objects.isNull(val)) {
//                    continue;
//                }
//                sb.append(tableList.getPrefixByClass(obj.getClass())).append(Constants.DOT)
//                        .append(fieldInfo.getColumn()).append(Constants.EQUALS).append(
//                                formatParam(AdapterHelper.getAdapter().mpjMapping(fieldInfo), val))
//                        .append(StrUtil.COMMA);
//            }
        }
    }

    @Override
    @SuppressWarnings("DuplicatedCode")
    public void clear() {
        super.clear();
        sqlSetStr.toNull();
        if (CollectionUtil.isNotEmpty(sqlSet)) {
            sqlSet.clear();
        }
        if (CollectionUtil.isNotEmpty(updateSet)) {
            updateSet.clear();
        }
        if (CollectionUtil.isNotEmpty(updateEntity)) {
            updateEntity.clear();
        }
        if (CollectionUtil.isNotEmpty(updateEntityNull)) {
            updateEntityNull.clear();
        }
    }


    @Data
    public static class UpdateSet {

        private SFunction<?, ?> column;

        private Object value;

        private String mapping;

        private boolean incOrDnc;

        private String cal;

        private boolean isApply = false;

        private String format;

        private SFunction<?, ?>[] columns;

        private Object[] args;

        public UpdateSet() {
        }

        public UpdateSet(SFunction<?, ?> column, Object value, String mapping, boolean incOrDnc, String cal) {
            this.column = column;
            this.value = value;
            this.mapping = mapping;
            this.incOrDnc = incOrDnc;
            this.cal = cal;
        }
    }
}
